1. 개요
파이썬의 리스트(List)는 여러 개의 데이터를 순서대로 저장할 수 있는 가변(Mutable) 시퀀스 자료형이다.
리스트는 다음과 같은 핵심 특징을 가진다.
- 가변성(Mutability): 생성 후에도 요소를 수정, 추가, 삭제할 수 있다.
- 순서 유지(Ordered): 요소들이 삽입된 순서가 유지되며, 인덱스를 통해 위치 기반 접근이 가능하다.
- 다양한 자료형 저장: 하나의 리스트 내에 정수, 문자열, 실수, 심지어 다른 리스트나 객체 등 서로 다른 타입의 데이터를 동시에 저장할 수 있다.
기본 선언 방법:
대괄호 []를 사용하거나 list() 생성자를 통해 선언한다.
# 빈 리스트 생성
empty_list = []
# 초기값이 있는 리스트 생성
fruits = ["apple", "banana", "cherry"]
# list() 생성자 사용
numbers = list((1, 2, 3))
2. 기본 조작 및 인덱싱
리스트의 각 요소는 고유한 번호인 인덱스(Index)를 가지며, 이를 통해 특정 데이터에 접근하거나 범위를 지정해 추출할 수 있다.
2.1 인덱싱 (Indexing)
- 양수 인덱스: 0부터 시작하여 왼쪽에서 오른쪽 방향으로 순서를 매긴다.
- 음수 인덱스: -1부터 시작하여 오른쪽에서 왼쪽 방향으로 순서를 매긴다. 리스트의 마지막 요소에 빠르게 접근할 때 유용하다.
2.2 슬라이싱 (Slicing)
list[start:stop:step] 형식을 사용하여 리스트의 일부분을 새로운 리스트로 추출하는 기법이다. stop 인덱스 직전까지의 요소가 포함된다.
- start: 시작 인덱스 (생략 시 0부터 시작)
- stop: 종료 인덱스 (생략 시 리스트 끝까지 포함)
- step: 증감 폭 (생략 시 1씩 증가)
nums = [0, 10, 20, 30, 40, 50]
# 인덱싱
print(nums[0]) # 0 (첫 번째 요소)
print(nums[-1]) # 50 (마지막 요소)
# 슬라이싱
print(nums[1:4]) # [10, 20, 30] (인덱스 1부터 3까지)
print(nums[:3]) # [0, 10, 20] (처음부터 인덱스 2까지)
print(nums[::2]) # [0, 20, 40] (2칸 간격으로 추출)
3. 주요 메서드 및 함수
3.1 내장 메서드
리스트 객체 자체에서 제공하는 메서드들이다.
| 메서드명 |
기능 |
시간 복잡도 |
비고 |
append(x) |
리스트 끝에 요소 x를 추가 |
O(1) |
가장 빈번하게 사용됨 |
insert(i, x) |
인덱스 i 위치에 요소 x를 삽입 |
O(n) |
삽입 위치 이후 요소들이 밀려남 |
extend(iterable) |
리스트 끝에 다른 반복 가능 객체를 확장 |
O(k) |
k는 추가되는 요소의 수 |
remove(x) |
리스트에서 첫 번째로 발견된 값 x를 삭제 |
O(n) |
값이 없으면 ValueError 발생 |
pop([i]) |
인덱스 i의 요소를 제거하고 반환 |
O(n) |
인덱스 지정 시 O(n), 미지정 시 O(1) |
clear() |
리스트의 모든 요소를 제거 |
O(n) |
빈 리스트 []가 됨 |
sort() |
리스트 요소를 오름차순으로 정렬 |
O(n log n) |
원본 리스트를 직접 변경 (In-place) |
reverse() |
리스트의 요소 순서를 반전 |
O(n) |
원본 리스트를 직접 변경 |
3.2 내장 함수
리스트를 인자로 받아 결과를 반환하는 파이썬 표준 함수들이다.
len(list): 리스트의 전체 요소 개수를 반환한다.
max(list): 리스트 내의 최댓값을 반환한다.
min(list): 리스트 내의 최솟값을 반환한다.
sum(list): 리스트 내 모든 수치 요소의 합계를 반환한다.
sorted(list): 리스트를 정렬한 새로운 리스트를 반환한다.
💡 sort() 메서드 vs sorted() 함수의 차이
- list.sort(): 원본 리스트의 순서를 직접 변경하며, 반환값은 None이다. (In-place 정렬)
- sorted(list): 원본 리스트는 그대로 유지하고, 정렬된 새로운 리스트 객체를 생성하여 반환한다.
4. 리스트 컴프리헨션 (List Comprehension)
리스트 컴프리헨션은 기존 리스트나 반복 가능한 객체를 바탕으로 새로운 리스트를 간결하게 생성하는 파이썬의 특수 문법이다.
구조: [표현식 for 항목 in 반복가능객체 if 조건문]
4.1 일반 for 루프와의 비교
# 목표: 1부터 10까지의 숫자 중 짝수의 제곱 리스트 생성
# 1. 일반 for 루프
even_squares = []
for x in range(1, 11):
if x % 2 == 0:
even_squares.append(x**2)
# 2. 리스트 컴프리헨션
even_squares_comp = [x**2 for x in range(1, 11) if x % 2 == 0]
print(even_squares_comp) # [4, 16, 36, 64, 100]
효율성: 리스트 컴프리헨션은 내부적으로 최적화되어 있어 일반적인
for 루프와
append() 호출 조합보다 실행 속도가 빠르며 코드의 가독성을 높인다.
5. 심화 활용 및 주의사항
리스트는 가변 객체이므로 단순 할당(list_b = list_a)을 하면 두 변수가 동일한 메모리 주소를 가리키게 된다. 이를 방지하기 위해 복사 기법을 사용한다.
- 얕은 복사 (Shallow Copy): 리스트 껍데기는 복사하지만, 내부의 중첩된 객체는 참조 주소만 복사한다. (
list.copy(), [:] 슬라이싱)
- 깊은 복사 (Deep Copy): 내부의 중첩된 객체까지 모두 완전히 새로운 객체로 복사한다. (
copy 모듈의 deepcopy() 사용)
import copy
# 중첩 리스트 생성
original = [[1, 2], [3, 4]]
# 복사 수행
shallow = original.copy()
deep = copy.deepcopy(original)
# 원본의 내부 요소 수정
original[0][0] = 99
print(f"Original: {original}") # [[99, 2], [3, 4]]
print(f"Shallow: {shallow}") # [[99, 2], [3, 4]] (내부 리스트 참조를 공유하여 함께 변경됨)
print(f"Deep: {deep}") # [[1, 2], [3, 4]] (완전히 독립적인 복사본으로 유지됨)
5.2 다차원 리스트 (Nested List)
리스트 내부에 또 다른 리스트를 포함하여 행렬과 같은 구조를 만들 수 있다.
- 접근 방법: list[row][col] 형태로 인덱스를 중첩하여 접근한다.
5.3 리스트 언패킹 (Unpacking)
리스트의 요소를 개별 변수에 한 번에 할당하는 기법이다. * (Asterisk) 연산자를 사용하면 가변적인 개수의 요소를 묶어서 처리할 수 있다.
fruits = ["apple", "banana", "cherry", "date"]
# 기본 언패킹
a, b, c, d = fruits
# 가변 언패킹 (Extended Iterable Unpacking)
first, *others, last = fruits
print(first) # "apple"
print(others) # ["banana", "cherry"]
print(last) # "date"
6. 시퀀스 자료형 비교 (List, Tuple, Set)
| 구분 |
리스트 (List) |
튜플 (Tuple) |
셋 (Set) |
| 기호 |
[] |
() |
{} |
| 가변성 |
가변 (Mutable) |
불변 (Immutable) |
가변 (Mutable) |
| 순서 |
유지 (Ordered) |
유지 (Ordered) |
유지 안 함 (Unordered) |
| 중복 허용 |
허용 |
허용 |
불허 (Unique) |
| 주요 용도 |
데이터의 수정/추가가 잦을 때 |
데이터 보호, 딕셔너리 키 사용 |
중복 제거, 집합 연산 |
7. 시간 복잡도 및 성능 분석
파이썬의 리스트는 내부적으로 동적 배열(Dynamic Array)로 구현되어 있다. 이는 필요에 따라 메모리 공간을 자동으로 확장하는 구조이다.
| 작업 |
시간 복잡도 |
설명 |
인덱스 접근 (list[i]) |
O(1) |
메모리 주소 계산을 통해 즉시 접근 |
끝에 추가 (append) |
O(1) |
분할 상환 시간 복잡도(Amortized) 기준 |
중간에 삽입/삭제 (insert, pop(i), del) |
O(n) |
삽입/삭제 후 나머지 요소들을 이동시켜야 함 |
요소 검색 (in 연산자) |
O(n) |
최악의 경우 모든 요소를 순회해야 함 |
정렬 (sort) |
O(n log n) |
Timsort 알고리즘 사용 |
슬라이싱 (list[a:b]) |
O(k) |
k는 슬라이싱 된 범위의 길이 |
# 파이썬 리스트 (Python List)
## 1. 개요
파이썬의 **리스트(List)**는 여러 개의 데이터를 순서대로 저장할 수 있는 가변(Mutable) 시퀀스 자료형이다.
리스트는 다음과 같은 핵심 특징을 가진다.
- **가변성(Mutability):** 생성 후에도 요소를 수정, 추가, 삭제할 수 있다.
- **순서 유지(Ordered):** 요소들이 삽입된 순서가 유지되며, 인덱스를 통해 위치 기반 접근이 가능하다.
- **다양한 자료형 저장:** 하나의 리스트 내에 정수, 문자열, 실수, 심지어 다른 리스트나 객체 등 서로 다른 타입의 데이터를 동시에 저장할 수 있다.
**기본 선언 방법:**
대괄호 `[]`를 사용하거나 `list()` 생성자를 통해 선언한다.
```python
# 빈 리스트 생성
empty_list = []
# 초기값이 있는 리스트 생성
fruits = ["apple", "banana", "cherry"]
# list() 생성자 사용
numbers = list((1, 2, 3))
```
## 2. 기본 조작 및 인덱싱
리스트의 각 요소는 고유한 번호인 **인덱스(Index)**를 가지며, 이를 통해 특정 데이터에 접근하거나 범위를 지정해 추출할 수 있다.
### 2.1 인덱싱 (Indexing)
- **양수 인덱스:** 0부터 시작하여 왼쪽에서 오른쪽 방향으로 순서를 매긴다.
- **음수 인덱스:** -1부터 시작하여 오른쪽에서 왼쪽 방향으로 순서를 매긴다. 리스트의 마지막 요소에 빠르게 접근할 때 유용하다.
### 2.2 슬라이싱 (Slicing)
`list[start:stop:step]` 형식을 사용하여 리스트의 일부분을 새로운 리스트로 추출하는 기법이다. `stop` 인덱스 직전까지의 요소가 포함된다.
- **start:** 시작 인덱스 (생략 시 0부터 시작)
- **stop:** 종료 인덱스 (생략 시 리스트 끝까지 포함)
- **step:** 증감 폭 (생략 시 1씩 증가)
```python
nums = [0, 10, 20, 30, 40, 50]
# 인덱싱
print(nums[0]) # 0 (첫 번째 요소)
print(nums[-1]) # 50 (마지막 요소)
# 슬라이싱
print(nums[1:4]) # [10, 20, 30] (인덱스 1부터 3까지)
print(nums[:3]) # [0, 10, 20] (처음부터 인덱스 2까지)
print(nums[::2]) # [0, 20, 40] (2칸 간격으로 추출)
```
## 3. 주요 메서드 및 함수
### 3.1 내장 메서드
리스트 객체 자체에서 제공하는 메서드들이다.
| 메서드명 | 기능 | 시간 복잡도 | 비고 |
| :--- | :--- | :---: | :--- |
| `append(x)` | 리스트 끝에 요소 `x`를 추가 | O(1) | 가장 빈번하게 사용됨 |
| `insert(i, x)` | 인덱스 `i` 위치에 요소 `x`를 삽입 | O(n) | 삽입 위치 이후 요소들이 밀려남 |
| `extend(iterable)` | 리스트 끝에 다른 반복 가능 객체를 확장 | O(k) | `k`는 추가되는 요소의 수 |
| `remove(x)` | 리스트에서 첫 번째로 발견된 값 `x`를 삭제 | O(n) | 값이 없으면 `ValueError` 발생 |
| `pop([i])` | 인덱스 `i`의 요소를 제거하고 반환 | O(n) | 인덱스 지정 시 O(n), 미지정 시 O(1) |
| `clear()` | 리스트의 모든 요소를 제거 | O(n) | 빈 리스트 `[]`가 됨 |
| `sort()` | 리스트 요소를 오름차순으로 정렬 | O(n log n) | 원본 리스트를 직접 변경 (In-place) |
| `reverse()` | 리스트의 요소 순서를 반전 | O(n) | 원본 리스트를 직접 변경 |
### 3.2 내장 함수
리스트를 인자로 받아 결과를 반환하는 파이썬 표준 함수들이다.
- `len(list)`: 리스트의 전체 요소 개수를 반환한다.
- `max(list)`: 리스트 내의 최댓값을 반환한다.
- `min(list)`: 리스트 내의 최솟값을 반환한다.
- `sum(list)`: 리스트 내 모든 수치 요소의 합계를 반환한다.
- `sorted(list)`: 리스트를 정렬한 **새로운 리스트를 반환**한다.
**💡 `sort()` 메서드 vs `sorted()` 함수의 차이**
- `list.sort()`: 원본 리스트의 순서를 직접 변경하며, 반환값은 `None`이다. (In-place 정렬)
- `sorted(list)`: 원본 리스트는 그대로 유지하고, 정렬된 새로운 리스트 객체를 생성하여 반환한다.
## 4. 리스트 컴프리헨션 (List Comprehension)
리스트 컴프리헨션은 기존 리스트나 반복 가능한 객체를 바탕으로 새로운 리스트를 간결하게 생성하는 파이썬의 특수 문법이다.
**구조:** `[표현식 for 항목 in 반복가능객체 if 조건문]`
### 4.1 일반 for 루프와의 비교
```python
# 목표: 1부터 10까지의 숫자 중 짝수의 제곱 리스트 생성
# 1. 일반 for 루프
even_squares = []
for x in range(1, 11):
if x % 2 == 0:
even_squares.append(x**2)
# 2. 리스트 컴프리헨션
even_squares_comp = [x**2 for x in range(1, 11) if x % 2 == 0]
print(even_squares_comp) # [4, 16, 36, 64, 100]
```
**효율성:** 리스트 컴프리헨션은 내부적으로 최적화되어 있어 일반적인 `for` 루프와 `append()` 호출 조합보다 실행 속도가 빠르며 코드의 가독성을 높인다.
## 5. 심화 활용 및 주의사항
### 5.1 얕은 복사 vs 깊은 복사
리스트는 가변 객체이므로 단순 할당(`list_b = list_a`)을 하면 두 변수가 동일한 메모리 주소를 가리키게 된다. 이를 방지하기 위해 복사 기법을 사용한다.
- **얕은 복사 (Shallow Copy):** 리스트 껍데기는 복사하지만, 내부의 중첩된 객체는 참조 주소만 복사한다. (`list.copy()`, `[:]` 슬라이싱)
- **깊은 복사 (Deep Copy):** 내부의 중첩된 객체까지 모두 완전히 새로운 객체로 복사한다. (`copy` 모듈의 `deepcopy()` 사용)
```python
import copy
# 중첩 리스트 생성
original = [[1, 2], [3, 4]]
# 복사 수행
shallow = original.copy()
deep = copy.deepcopy(original)
# 원본의 내부 요소 수정
original[0][0] = 99
print(f"Original: {original}") # [[99, 2], [3, 4]]
print(f"Shallow: {shallow}") # [[99, 2], [3, 4]] (내부 리스트 참조를 공유하여 함께 변경됨)
print(f"Deep: {deep}") # [[1, 2], [3, 4]] (완전히 독립적인 복사본으로 유지됨)
```
### 5.2 다차원 리스트 (Nested List)
리스트 내부에 또 다른 리스트를 포함하여 행렬과 같은 구조를 만들 수 있다.
- **접근 방법:** `list[row][col]` 형태로 인덱스를 중첩하여 접근한다.
### 5.3 리스트 언패킹 (Unpacking)
리스트의 요소를 개별 변수에 한 번에 할당하는 기법이다. `*` (Asterisk) 연산자를 사용하면 가변적인 개수의 요소를 묶어서 처리할 수 있다.
```python
fruits = ["apple", "banana", "cherry", "date"]
# 기본 언패킹
a, b, c, d = fruits
# 가변 언패킹 (Extended Iterable Unpacking)
first, *others, last = fruits
print(first) # "apple"
print(others) # ["banana", "cherry"]
print(last) # "date"
```
## 6. 시퀀스 자료형 비교 (List, Tuple, Set)
| 구분 | 리스트 (List) | 튜플 (Tuple) | 셋 (Set) |
| :--- | :--- | :--- | :--- |
| **기호** | `[]` | `()` | `{}` |
| **가변성** | 가변 (Mutable) | 불변 (Immutable) | 가변 (Mutable) |
| **순서** | 유지 (Ordered) | 유지 (Ordered) | 유지 안 함 (Unordered) |
| **중복 허용** | 허용 | 허용 | 불허 (Unique) |
| **주요 용도** | 데이터의 수정/추가가 잦을 때 | 데이터 보호, 딕셔너리 키 사용 | 중복 제거, 집합 연산 |
## 7. 시간 복잡도 및 성능 분석
파이썬의 리스트는 내부적으로 **동적 배열(Dynamic Array)**로 구현되어 있다. 이는 필요에 따라 메모리 공간을 자동으로 확장하는 구조이다.
| 작업 | 시간 복잡도 | 설명 |
| :--- | :---: | :--- |
| 인덱스 접근 (`list[i]`) | O(1) | 메모리 주소 계산을 통해 즉시 접근 |
| 끝에 추가 (`append`) | O(1) | 분할 상환 시간 복잡도(Amortized) 기준 |
| 중간에 삽입/삭제 (`insert`, `pop(i)`, `del`) | O(n) | 삽입/삭제 후 나머지 요소들을 이동시켜야 함 |
| 요소 검색 (`in` 연산자) | O(n) | 최악의 경우 모든 요소를 순회해야 함 |
| 정렬 (`sort`) | O(n log n) | Timsort 알고리즘 사용 |
| 슬라이싱 (`list[a:b]`) | O(k) | `k`는 슬라이싱 된 범위의 길이 |